/**
 **oj/file - File operations
 *
 * The file module provides a File object for reading and writing to a file. A File
 * instance can be created directly or through use of the open function. 
 *
 */

define(['oj/os'], function(os) {
        
    window['file'] = {}
        
    var CONCAT_LIMIT = 90,
        URI_LIMIT = 1801
    
    
    file['open'] = function(path) {
        /**
         *> file.open('path')
         *
         * Returns a File instance.
         *
         *. var f = file.open('file.js');
         *
         */
        
        return (new file.File(path))
    }
    
    
    file['File'] = function(path) {
        /**
         ** File - File object
         */
        
        var self = this,
            closed = false
        
        self.path = os.path.abspath(path)
        self.content = ''
    
    
        self.read = function (callback) {
            /**
             *> read(callback)
             *
             * Reads the file. In addition to the standard response properties, this
             * instance of File is returned as 'file'.
             *
             *. f.read(function (msg) {
             *.     if (!oj.isError(msg)) {
             *.         var read = msg.file.content;
             *.     }
             *. }
             * 
             */
    
            os.read(self.path, function (response) {
                if (response.hasOwnProperty('error')) {
                    if (typeof callback === 'function') {
                        response.file = self
                        callback(response)
                    }
                    return
                }
                self.content = response
                closed = false
                if (typeof callback === 'function') {
                    response = {
                        success: 'successfully read',
                        file: self
                    };
                    callback(response)
                }
            })
    
            return self
        }
    
    
        self.write = function (string) {
            /**
             *> write('string')
             *
             * Write a string to the file. Due to buffering the string will not appear
             * in the server version of the file until flush() or close() are called.
             *
             *. f.write('append this to the files content');
             * 
             */
    
            self.content += string
        }
    
    
        self.flush = function (callback) {
            /**
             *> flush(callback)
             *
             * Flush the internal buffer.
             
             */
    
            var MAX_LEN = URI_LIMIT - self.path.length,
                data = encodeURIComponent(self.content),
                len, parts = [],
                resps = 0,
                i
    
            // chop data up into parts for sending 
            while (data.length > MAX_LEN) {
                len = MAX_LEN
    
                // care we don't chop escapes (~X) or encodings (%40, %C3)
                while (data.charAt(len - 1) === '~')
					len--
                while (data.lastIndexOf('%', len) > len - 3)
                    len = data.lastIndexOf('%', len) - 1
                parts.push(data.slice(0, len))
                data = data.slice(len)
            }
    
            parts.push(data)
    
            var concatParts = function (concat) {
                if (typeof concat === 'undefined' || concat.hasOwnProperty('success')) {
                    if (parts.length > 0) {
                        i = parts.slice(0, CONCAT_LIMIT)
                        parts = parts.slice(CONCAT_LIMIT)
                        if (parts.length > 0) {
                            resps = i[i.length - 1] + '.0'
                            i.push(resps)
                            parts.unshift(resps)
                        }
                        os.concat(i, true, concatParts)
                        return
                    }
                    if (typeof callback === 'function') callback({
                        success: true,
                        file: self
                    })
                    return
                }
                if (typeof callback === 'function') {
                    concat.file = self
                    callback(concat)
                }
            }
            
            var multiWrite = function (write) {
                if (write.hasOwnProperty('error')) {
                    resps += parts.length + 1
                    if (typeof callback === 'function') {
                        write.file = self
                        callback(write)
                    }
                }
                if (typeof callback === 'function')
					callback({
                        multipart: resps,
                        file: self
                    });
                
                if (++resps !== parts.length)
					return

                for (i = 0; i < parts.length; i++)
                    parts[i] = '/tmp' + self.path + '.' + i

                parts.push(self.path)
				concatParts()
            }
            
            if (parts.length > 1) {
                if (typeof callback === 'function')
					callback({
                        multiparts: parts.length
                    });

                // multipart write
                for (i = 0; i < parts.length; i++)
                    os.write('/tmp' + self.path + '.' + i, parts[i], multiWrite)

                return
            }
    
            // single part write
            os.write(self.path, parts[0], function (write) {
                if (typeof callback === 'function') {
                    if (write.hasOwnProperty('error')) {
                        write.file = self
                        return callback(write)
                    }
                    callback({
                        success: true,
                        file: self
                    })
                }
            })
        }
    
    
        self.close = function (callback) {
            /**
             *> close(callback)
             *
             * Close the file. A closed file cannot be read or written to any more.
             */
    
            self.flush(function (m) {
                if (m.hasOwnProperty('success'))
				    closed = true

                if (typeof callback === 'function')
				    callback(m)
            })
        }
    
    
        self.closed = function () {
            /**
             *> closed()
             *
             * Returns true if file is closed; otherwise false.
             
             */
    
            return closed
        }
    
    
        self.truncate = function () {
            /**
             *> truncate()
             *
             * Truncates the files contents to an empty string.
             *
             */
    
            self.content = ''
        }
    
    }
    
    return file
    
})
